import Redis from 'ioredis';
import config from '../config';
import { logToDatabase, LogType, LogLevel, consoleLog } from '../utils/logger';
import { utils } from '../utils/utils';

let redisClient: Redis;

/**
 * 初始化Redis连接
 */
export function initRedis(): Redis {
  if (!redisClient) {
    redisClient = new Redis({
      host: config.redis.host,
      port: config.redis.port,
      password: config.redis.password || undefined,
      db: config.redis.db,
      // 连接配置
      connectTimeout: 10000, // 10秒连接超时
      commandTimeout: 5000,  // 5秒命令超时
      lazyConnect: false,    // 立即连接
      keepAlive: 30000,      // 30秒 keep-alive，防止连接被关闭
      // 重试策略
      retryStrategy: (times) => {
        // 最多重试 10 次
        if (times > 10) {
          return null; // 停止重试
        }
        const delay = Math.min(times * 50, 2000);
        return delay;
      },
      maxRetriesPerRequest: 3, // 每个请求最多重试 3 次
      enableReadyCheck: true,  // 启用就绪检查
      enableOfflineQueue: true, // 启用离线队列，连接断开时缓存命令
      // 自动重连配置
      reconnectOnError: (err: Error) => {
        const targetError = 'READONLY';
        // 如果是只读错误，不自动重连
        if (err.message.includes(targetError)) {
          return false;
        }
        // 其他错误自动重连
        return true;
      },
    });

    redisClient.on('error', (err) => {
      // ECONNRESET 通常是正常的重连过程，只记录为 WARN 级别
      // 其他错误记录为 ERROR 级别
      const errWithCode = err as Error & { code?: string };
      const isConnectionReset = err.message?.includes('ECONNRESET') || 
                                errWithCode.code === 'ECONNRESET';
      const level = isConnectionReset ? LogLevel.WARN : LogLevel.ERROR;
      const message = isConnectionReset 
        ? `Redis连接重置（自动重连中）: ${err.message}`
        : `Redis连接错误: ${err.message}`;
      
      // 只在非 ECONNRESET 错误时记录到数据库，避免日志过多
      if (!isConnectionReset) {
        logToDatabase(
          utils.generateUUID(),
          LogType.REDIS,
          level,
          message,
          {
            host: config.redis.host,
            port: config.redis.port,
            db: config.redis.db,
            error: {
              name: err.name,
              message: err.message,
              code: errWithCode.code,
              stack: err.stack,
            },
          }
        ).catch(() => {});
      }
      
      // ECONNRESET 错误不打印到控制台，避免日志过多
      if (!isConnectionReset) {
        consoleLog(
          LogType.REDIS,
          level,
          message,
          {
            error: err,
            host: config.redis.host,
            port: config.redis.port,
          }
        );
      }
    });

    redisClient.on('connect', () => {
      const requestId = utils.generateUUID();
      logToDatabase(
        requestId,
        LogType.REDIS,
        LogLevel.INFO,
        'Redis连接成功',
        {
          host: config.redis.host,
          port: config.redis.port,
          db: config.redis.db,
        }
      ).catch(() => {});
      consoleLog(
        LogType.REDIS,
        LogLevel.INFO,
        'Redis连接成功',
        {
          requestId,
          host: config.redis.host,
          port: config.redis.port,
          db: config.redis.db,
        }
      );
    });

    redisClient.on('ready', () => {
      logToDatabase(
        utils.generateUUID(),
        LogType.REDIS,
        LogLevel.INFO,
        'Redis已就绪',
        {
          host: config.redis.host,
          port: config.redis.port,
          db: config.redis.db,
        }
      ).catch(() => {});
    });

    redisClient.on('close', () => {
      logToDatabase(
        utils.generateUUID(),
        LogType.REDIS,
        LogLevel.WARN,
        'Redis连接已关闭',
        {
          host: config.redis.host,
          port: config.redis.port,
          db: config.redis.db,
        }
      ).catch(() => {});
    });

    redisClient.on('reconnecting', (delay: number) => {
      logToDatabase(
        utils.generateUUID(),
        LogType.REDIS,
        LogLevel.WARN,
        `Redis正在重连，延迟 ${delay}ms`,
        {
          host: config.redis.host,
          port: config.redis.port,
          db: config.redis.db,
          delay,
        }
      ).catch(() => {});
    });
  }
  return redisClient;
}

/**
 * 获取Redis客户端
 */
export function getRedisClient(): Redis {
  if (!redisClient) {
    return initRedis();
  }
  return redisClient;
}

/**
 * 执行Redis命令（带日志记录）
 */
export async function redisCommand<T = any>(
  command: string,
  args: any[] = [],
  requestId?: string
): Promise<T> {
  const start = Date.now();
  const logRequestId = requestId || utils.generateUUID();
  const client = getRedisClient();
  
  try {
    const result = await client.call(command, ...args);
    const duration = Date.now() - start;
    
    // 记录成功的命令日志
    await logToDatabase(
      logRequestId,
      LogType.REDIS,
      LogLevel.INFO,
      `Redis命令执行成功 - ${command} - ${duration}ms`,
      {
        command,
        args: JSON.stringify(args),
        duration,
      }
    ).catch(() => {});
    
    return result as T;
  } catch (err: any) {
    const duration = Date.now() - start;
    
    // 判断错误类型
    let level = LogLevel.ERROR;
    let message = `Redis命令执行失败: ${err.message}`;
    
    if (err.message?.includes('timeout') || err.message?.includes('TIMEOUT')) {
      message = `Redis命令超时: ${command} - ${err.message}`;
    } else if (err.message?.includes('ECONNREFUSED') || err.message?.includes('ENOTFOUND')) {
      message = `Redis连接失败: ${err.message}`;
    }
    
    // 记录错误日志
    await logToDatabase(
      logRequestId,
      LogType.REDIS,
      level,
      message,
      {
        command,
        args: JSON.stringify(args),
        duration,
        error: {
          name: err.name,
          message: err.message,
          stack: err.stack,
        },
      }
    ).catch(() => {});
    
    throw err;
  }
}

/**
 * 测试Redis连接
 */
export async function testRedisConnection(): Promise<boolean> {
  try {
    const client = getRedisClient();
    await client.ping();
    
    // 记录连接测试成功
    await logToDatabase(
      utils.generateUUID(),
      LogType.REDIS,
      LogLevel.INFO,
      'Redis连接测试成功',
      {
        host: config.redis.host,
        port: config.redis.port,
        db: config.redis.db,
      }
    ).catch(() => {});
    
    return true;
  } catch (error: any) {
    // 记录连接测试失败
    await logToDatabase(
      utils.generateUUID(),
      LogType.REDIS,
      LogLevel.ERROR,
      `Redis连接测试失败: ${error.message}`,
      {
        host: config.redis.host,
        port: config.redis.port,
        db: config.redis.db,
        error: {
          name: error.name,
          message: error.message,
        },
      }
    ).catch(() => {});
    
    consoleLog(
      LogType.REDIS,
      LogLevel.ERROR,
      `Redis连接测试失败: ${error?.message || String(error)}`,
      {
        error: error,
        host: config.redis.host,
        port: config.redis.port,
        db: config.redis.db,
      }
    );
    return false;
  }
}

